package base.tips;

import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.CharBuffer;
import java.util.HashMap;
import java.util.Map;

// Replace Strings in Streams, Arrays, Files etc.
// 技巧1 :　替换流，数组，文件等中的字符串
public class ReplaceStringsDemo {
    public static void main(String[] args) {
        // 有时您需要替换流，数组，文件或大字符串中的字符串或标记。

        String data = " 12345612 ";
        String  stringStream = "1234567890${token1}abcdefg${token2}XYZ$000";

        //  1. 传统方式
        traditionRreplace(data);

        // 2. Reader Stream
        tokenReplacingReader(stringStream);

    }

    // 1. 传统替换方法
    public static void traditionRreplace(String data) {
        // 太麻烦
        String newData = data.replace("12", "ab")
                .replace("34", "cd")
                .replace("56", "ef");

        System.out.println(newData);
    }

    // 2. TokenReplacingReader
    public static void tokenReplacingReader(String  stringStream) {
        Map<String, String> tokens = new HashMap<String, String>();
        tokens.put("token1", "value1");
        tokens.put("token2", "JJ BOCKS");

        MapTokenResolver resolver = new MapTokenResolver(tokens);
        Reader source = new StringReader(stringStream);

        Reader reader = new TokenReplacingReader(source, resolver);
        int data = 0;
        try {
            data = reader.read();
            while (data != -1) {
                System.out.print((char) data);
                data = reader.read();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

class TokenReplacingReader extends Reader {
    protected PushbackReader pushbackReader = null;
    protected ITokenResolver tokenResolver = null;
    protected StringBuilder tokenNameBuffer = new StringBuilder();
    protected String tokenValue = null;
    protected int tokenValueIndx = 0;

    public TokenReplacingReader(Reader source , ITokenResolver resolver) {
        this.pushbackReader = new PushbackReader(source);
        this.tokenResolver = resolver;
    }

    // 尝试将字符读入指定的字符缓冲区。
    public int read(CharBuffer target) throws IOException {
        throw new RuntimeException("Operation Not Supported");
    }

    // 读取单个字符。
    public int read() throws IOException {
        if (this.tokenValue != null) {
            if (this.tokenValueIndx < this.tokenValue.length()) {
                return this.tokenValue.charAt(this.tokenValueIndx++);
            }

            if (this.tokenValueIndx == this.tokenValue.length()) {
                this.tokenValue = null;
                this.tokenValueIndx = 0;
            }
        }

        int data = this.pushbackReader.read();
        if (data != '$') {
            return data;
        }

        data = this.pushbackReader.read();
        if (data != '{') {
            this.pushbackReader.unread(data);
            return '$';
        }

        data = this.pushbackReader.read();
        while (data != '}') {
            this.tokenNameBuffer.append(data);
            data = this.pushbackReader.read();
        }

        this.tokenValue = this.tokenResolver.resolveToken(this.tokenNameBuffer.toString());

        if (this.tokenValue == null) {
            this.tokenValue = "${" + this.tokenNameBuffer.toString() + "}";
        }

        if (this.tokenValue.length() == 0) {
            return read();
        }

        return this.tokenValue.charAt(this.tokenValueIndx++);
    }

    // Reads characters into an array 将字符读入数组。
    public int read(char cbuf[]) throws IOException {
        return read(cbuf, 0, cbuf.length);
    }

    // 必须实现的方法
    // Reads characters into a portion of an array. 将字符读入数组的一部分。
    @Override
    public int read(char[] cbuf, int off, int len) throws IOException {
        int charsRead = 0;
        for (int i = 0; i < len; i++) {
            int nextChar = read();
            if (nextChar == -1) {
                if (charsRead == 0) {
                    charsRead = -1;
                }
                break;
            }
            charsRead = i + 1;
            cbuf[off + i] = (char) nextChar;
        }
        return charsRead;
    }

    // 必须实现的方法
    @Override
    public void close() throws IOException {
        this.pushbackReader.close();
    }

    // Skips characters. 跳过字符。该方法将阻塞，直到某些字符可用，发生I / O错误或到达流的末尾为止。
    // P 要跳过的字符数
    // R 实际跳过的字符数
    public long skip(long n) throws IOException {
        throw new RuntimeException("Operation Not Supported");
    }

    public boolean readay() throws IOException {
        return this.pushbackReader.ready();
    }

    public boolean markSupported() {
        return false;
    }

    // 标记流中的当前位置
    public void mark(int readAheadLimit) {
        throw new RuntimeException("Operation Not Supported");
    }

    // 重置流。如果流已被标记，则尝试将其重新放置在标记处。
    // 如果尚未标记流，则尝试以适合于特定流的某种方式将其重置，
    // 例如，将其重新放置到其起点。并非所有的字符输入流都支持reset（）操作，并且某些字符输入流不支持mark（）。
    public void reset() throws IOException {
        throw new RuntimeException("Operation Not Supported");
    }
}

interface ITokenResolver {
     String resolveToken(String tokenName);
}

class MapTokenResolver implements ITokenResolver {
    protected Map<String, String> tokenMap = new HashMap<String, String>();

    public MapTokenResolver(Map<String, String> tokenMap) {
        this.tokenMap = tokenMap;
    }

    @Override
    public String resolveToken(String tokenName) {
        return this.tokenMap.get(tokenName);
    }

}
